#include "Character.h"
#include "Item.h"
#include "Room.h"
#include "World.h"
#include "Items\Corpse.h"

#include "../ConsoleColor.h"
#include "../StringHelper.h"
#include <iostream>

#include "../Components/Eyes.h"
#include "../Components/Hands.h"
#include "../Components/Feet.h"
#include "../Components/Pockets.h"
#include "../Components/Vitality.h"

#include "../Player.h"
#include "../Random.h"

Character::Character(const std::string &name, const std::string &description, Character::Sex sex, unsigned int bodyparts, unsigned int weight) 
	: GameObject(name,description), sex(sex), bodyparts(bodyparts), stance(STANDING)
{
	this->weight = weight; //Average character is 70 kg.

	//A character's components
	GameObject::add(std::make_shared<Eyes>());
	GameObject::add(std::make_shared<Hands>());
	GameObject::add(std::make_shared<Feet>());
	GameObject::add(std::make_shared<Pockets>());
	
	auto vitality = GameObject::add<Vitality>(std::make_shared<Vitality>());
	if(bodyparts & HEAD) vitality->addBodypart(HEAD, 100, 50, 5);
	if(bodyparts & THROAT) vitality->addBodypart(THROAT, 100, 40, 2);
	if(bodyparts & LEFT_ARM) vitality->addBodypart(LEFT_ARM, 100, 5, 1);
	if(bodyparts & RIGHT_ARM) vitality->addBodypart(RIGHT_ARM, 100, 5, 1);
	if(bodyparts & CHEST) vitality->addBodypart(CHEST, 100, 20, 3);
	if(bodyparts & STOMACK) vitality->addBodypart(STOMACK, 100, 30, 2);
	if(bodyparts & GROIN) vitality->addBodypart(GROIN, 100, 25, 1);
	if(bodyparts & LEFT_LEG) vitality->addBodypart(LEFT_LEG, 100, 10, 4);
	if(bodyparts & RIGHT_LEG) vitality->addBodypart(RIGHT_LEG, 100, 10, 4);
	if(bodyparts & LEFT_FOOT) vitality->addBodypart(LEFT_FOOT, 100, 2, 2);
	if(bodyparts & RIGHT_FOOT) vitality->addBodypart(RIGHT_FOOT, 100, 2, 2);
	vitality->registerDieCallback([](CharacterPtr character, const RoomPtr &room) 
	{ 
		if(character->hasPlayer())
			std::cout << "Sorry, you're dead!" << std::endl;
		else
		{
			std::cout << "Your spine freeze as you hear the death cry of " << character->getIndefiniteArticle() << " " << character->getName() << "!" << std::endl;
			std::cout << StringHelper::toUpper(character->getIndefiniteArticle()) << " " << character->getName() << " is dead." << std::endl;
		}

		room->remove(character);
		room->add(std::make_shared<Corpse>(character->getName(), character->getWeight()));

		if(character->hasPlayer())
		{
			character->getPlayer()->setRoom(World::getSingleton()->getStartRoom());
			World::getSingleton()->getStartRoom()->enter(character, Exit::NULL_EXIT);
		}
	});
	vitality->registerDamageCallback([](const CharacterPtr &character, Character::BodyPart bodypart, unsigned int /*dmg*/) 
	{ 
		if(character->hasPlayer())
			std::cout << "You cry out in pain, clutching your " << Character::getBodypartName(bodypart) << "!" << std::endl;
		else
			std::cout << StringHelper::toUpper(character->getIndefiniteArticle()) << " " << character->getName() << " cries out in pain, clutching " << character->getSexAsString() << " " << Character::getBodypartName(bodypart) << "!" << std::endl;
	});
}

Character::~Character()
{
}

void Character::look() 
{ 
	std::string stance = getStanceAsString();
	if(stance.empty() == false)
		stance = " " + stance;

	std::string health_report = getComponent<Vitality>("Vitality")->getHealthReport(this);

	std::cout << StringHelper::toUpper(getIndefiniteArticle()) << " " << red << name << white << stance << ". " << health_report << std::endl;
}

void Character::examine() 
{ 
	GameObject::examine();
	if(hasComponent("Vitality"))
	{
		std::string report = getComponent<Vitality>("Vitality")->getHealthReport(this, true);
		std::cout << report << std::endl;
	}
}

void Character::add(const ItemPtr &item)
{
	items.push_back(item);
}

void Character::remove(const ItemPtr &item)
{
	for(unsigned int i = 0; i < items.size(); i++)
	{
		if(item->getName() == items[i]->getName())
		{
			items[i] = items.back();
			items.pop_back();
			break;
		}
	}
}

bool Character::hold(const ItemPtr &item)
{
	if(held_item == nullptr)
	{
		held_item = item;
		if(hasPlayer())
			std::cout << "You hold " << getIndefiniteArticle() << " " << item->getName() << std::endl;
		else
		{
			std::cout << name << " grabs " << getIndefiniteArticle() << " " << item->getName() << " from his pockets." << std::endl;
		}
		return true;
	}
	else if(hasPlayer())
	{
		std::cout << "You're already holding something!" << std::endl;
	}
	return false;
}

bool Character::wield(const ItemPtr &item)
{
	if(held_item == nullptr)
	{
		held_item = item;
		if(hasPlayer())
			std::cout << "You wield " << getIndefiniteArticle() << " " << item->getName() << ", ready to protect the weak!" << std::endl;
		else
		{
			std::cout << name << " draws " << getIndefiniteArticle() << " " << item->getName() << " from his sheath." << std::endl;
		}
		return true;
	}
	else if(hasPlayer())
	{
		std::cout << "You're already holding something!" << std::endl;
	}
	return false;
}

ItemPtr Character::removeItemInHand()
{
	ItemPtr return_item = held_item;
	if(held_item != nullptr)
		held_item.reset();
	return return_item;
}

std::string Character::getSexAsString() const
{
	switch(sex)
	{
	case HE:
		return "he";
	case SHE:
		return "she";
	case IT:
		return "it";
	default:
		return "he";
	};
}

std::string Character::getStanceAsString()
{
	switch(stance)
	{
	case LAYING: return "lies here";
	case SLEEPING: return "is sleeping here";
	case RESTING: return "rests here";
	case SITTING: return "is sitting here";
	case FLYING: return "hovers in mid air";
	case STANDING: return "is here";
	case UNCONSCIOUS: return "lies here, unconscious";
	default: return "is here";
	};
}

bool Character::canHandleCommand(const CommandPtr &command)
{
	//Let's say that a character can hold an item, and that item's abilities
	//can allow a character to perform new commands...
	if(held_item)
	{
		if(held_item->canHandleCommand(command))
			return true;
	}

	//Then let's check if any inventory items can handle the command,
	//like a weapon will have the "wield" command for instance.
	for(unsigned int i = 0; i < items.size(); i++)
	{
		if(items[i]->canHandleCommand(command))
			return true;
	}

	if(GameObject::canHandleCommand(command))
		return true;

	return false;
}

Character::BodyPart Character::getRandomBodypart(int seed, int call_count) const
{
	BodyPart return_part = BODYPART_ERROR;
	/*if(seed < 0)
	{
		seed = World::getSingleton()->getCurrentClock();
		srand(seed);
	}
	else
		srand(seed);*/

	int rand_num = Random::getSingleton()->rand(12);
	switch(rand_num)
	{
	case 0: return_part = (bodyparts & HEAD) ? HEAD : BODYPART_ERROR; break;
	case 1: return_part =  (bodyparts & THROAT) ? THROAT : BODYPART_ERROR; break;
	case 2: return_part =  (bodyparts & LEFT_ARM) ? LEFT_ARM : BODYPART_ERROR; break;
	case 3: return_part =  (bodyparts & RIGHT_ARM) ? RIGHT_ARM : BODYPART_ERROR; break;
	case 4: return_part =  (bodyparts & CHEST) ? CHEST : BODYPART_ERROR; break;
	case 5: return_part =  (bodyparts & STOMACK) ? STOMACK : BODYPART_ERROR; break;
	case 6: return_part =  (bodyparts & SPINE) ? SPINE : BODYPART_ERROR; break;
	case 7: return_part =  (bodyparts & GROIN) ? GROIN : BODYPART_ERROR; break;
	case 8: return_part =  (bodyparts & LEFT_LEG) ? LEFT_LEG : BODYPART_ERROR; break;
	case 9: return_part =  (bodyparts & RIGHT_LEG) ? RIGHT_LEG : BODYPART_ERROR; break;
	case 10: return_part =  (bodyparts & LEFT_FOOT) ? LEFT_FOOT : BODYPART_ERROR; break;
	case 11: return_part =  (bodyparts & RIGHT_FOOT) ? RIGHT_FOOT : BODYPART_ERROR; break;
	default: return_part = BODYPART_ERROR; break;
	};

	if(return_part == BODYPART_ERROR && call_count < 10)
		return_part = getRandomBodypart(seed++, call_count++);
	else if(return_part == BODYPART_ERROR)
		return HEAD;

	return return_part;
}

std::string Character::getBodypartName(BodyPart bodypart)
{
	switch(bodypart)
	{
	case HEAD: return "head";
	case THROAT: return "throat";
	case LEFT_ARM: return "left arm";
	case RIGHT_ARM: return "right arm";
	case CHEST: return "chest";
	case STOMACK: return "stomack";
	case SPINE: return "spine";
	case GROIN: return "groin";
	case LEFT_LEG: return "left leg";
	case RIGHT_LEG: return "right leg";
	case LEFT_FOOT: return "left foot";
	case RIGHT_FOOT: return "right foot";
	default: return "unknown";
	};
}
